Get Fire Perimeters from Web Feature Service

Explore data available through a web feature service, and how to filter data temporally, spatially, and by property.
Author

Tempest McCabe, Julia Signell

Published

March 8, 2023

Run this notebook

You can launch this notbook using mybinder, by clicking the button below.

Binder

Approach

  1. Use OWSLib to determine what data is available and inspect the metadata
  2. Use OWSLib to filter and read the data
  3. Use geopandas and folium to analyze and plot the data

Note that the default examples environment is missing one requirement: oswlib. We can pip install that before we move on.

!pip install OWSLib==0.28.1
Requirement already satisfied: OWSLib==0.28.1 in /srv/conda/envs/notebook/lib/python3.10/site-packages (0.28.1)
Requirement already satisfied: pyyaml in /srv/conda/envs/notebook/lib/python3.10/site-packages (from OWSLib==0.28.1) (5.4.1)
Requirement already satisfied: pytz in /srv/conda/envs/notebook/lib/python3.10/site-packages (from OWSLib==0.28.1) (2022.7)
Requirement already satisfied: requests>=1.0 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from OWSLib==0.28.1) (2.28.1)
Requirement already satisfied: lxml in /srv/conda/envs/notebook/lib/python3.10/site-packages (from OWSLib==0.28.1) (4.9.2)
Requirement already satisfied: python-dateutil>=1.5 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from OWSLib==0.28.1) (2.8.2)
Requirement already satisfied: six>=1.5 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from python-dateutil>=1.5->OWSLib==0.28.1) (1.16.0)
Requirement already satisfied: idna<4,>=2.5 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from requests>=1.0->OWSLib==0.28.1) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from requests>=1.0->OWSLib==0.28.1) (1.26.14)
Requirement already satisfied: charset-normalizer<3,>=2 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from requests>=1.0->OWSLib==0.28.1) (2.1.1)
Requirement already satisfied: certifi>=2017.4.17 in /srv/conda/envs/notebook/lib/python3.10/site-packages (from requests>=1.0->OWSLib==0.28.1) (2022.12.7)
from owslib.ogcapi.features import Features
import geopandas as gpd

Look at the data that is availible through the Web Feature Service (WFS)

The datasets that are distributed throught the WFS are organized into collections. We can display the collections with the command:

WFS_URL = "https://firenrt.delta-backend.com"

w = Features(url=WFS_URL)
w.feature_collections()
['public.eis_fire_fireline',
 'public.eis_fire_newfirepix',
 'public.eis_fire_newfirepix_2',
 'public.eis_fire_perimeter',
 'public.eis_2023_perimeter',
 'public.st_subdivide',
 'public.st_hexagongrid',
 'public.st_squaregrid']

We will focus on the public.eis_fire_fireline collection and the public.eis_fire_perimeter collection.

Inspect the metatdata for public.eis_fire_perimeter collection

We can access information that drescribes the public.eis_fire_perimeter.

perm = w.collection("public.eis_fire_perimeter")

We are particularly interested in the spatial and temporal extents of the data.

perm["extent"]
{'spatial': {'bbox': [[-123.99217987060547,
    31.054306030273438,
    -101.49222564697266,
    49.461177825927734]],
  'crs': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84'},
 'temporal': {'interval': [['2019-05-01T00:00:00+00:00',
    '2020-10-31T12:00:00+00:00']],
  'trs': 'http://www.opengis.net/def/uom/ISO-8601/0/Gregorian'}}

In addition to getting metadata about the data we can access the queryable fields. Each of these fields will represent a column in our dataframe.

perm_q = w.collection_queryables('public.eis_fire_perimeter')
perm_q['properties']
{'wkb_geometry': {'$ref': 'https://geojson.org/schema/Geometry.json'},
 'ogc_fid': {'name': 'ogc_fid', 'type': 'number'},
 'n_pixels': {'name': 'n_pixels', 'type': 'number'},
 'n_newpixels': {'name': 'n_newpixels', 'type': 'number'},
 'farea': {'name': 'farea', 'type': 'number'},
 'fperim': {'name': 'fperim', 'type': 'number'},
 'flinelen': {'name': 'flinelen', 'type': 'number'},
 'duration': {'name': 'duration', 'type': 'number'},
 'pixden': {'name': 'pixden', 'type': 'number'},
 'meanfrp': {'name': 'meanfrp', 'type': 'number'},
 't': {'name': 't', 'type': 'string'},
 'fid': {'name': 'fid', 'type': 'number'}}

Filter the data

It is always a good idea to do any data filtering as early as possible. In this example we know that we want the data for particular spatial and temporal extents. We can apply those and other filters using the OWSLib package.

In the below example we are:

  • choosing the public.eis_fire_perimeter collection
  • subsetting it by space using the bbox parameter
  • subsetting it by time using the datetime parameter
  • filtering for fires over 5km^2 and over 20 days long using the filter parameter. The filter parameter lets us filter by the columns in ‘public.eis_fire_perimeter’ using SQL-style queries.

NOTE: The limit parameter desginates the maximum number of objects the query will return. The default limit is 10, so if we want to all of the fire perimeters within certain conditions, we need to make sure that the limit is large.

perm_results = w.collection_items(
    "public.eis_fire_perimeter",  # name of the dataset we want
    bbox=["-119.5", "36.8", "-118.9", "37.7"],  # coordinates of bounding box, 
    datetime=["2020-01-01T00:00:00+00:00/2020-10-31T12:00:00+00:00"],  # date range
    limit=1000,  # max number of items returned
    filter="farea>5 AND duration>20",  # additional filters based on queryable fields
)

The result is a dictionary containing all of the data and some summary fields. We can look at the keys to see what all is in there.

perm_results.keys()
dict_keys(['type', 'id', 'title', 'description', 'numberMatched', 'numberReturned', 'links', 'features'])

For instance you can check the total number of matched items and make sure that it is equal to the number of returned items. This is how you know that the limit you defined above is high enough.

perm_results['numberMatched'] == perm_results["numberReturned"]
True

You can also access the data directly in the browser or in an HTTP GET call using the constructed link.

perm_results['links'][1]['href']
'https://firenrt.delta-backend.com/collections/public.eis_fire_perimeter/items?bbox=-119.5%2C36.8%2C-118.9%2C37.7&datetime=2020-01-01T00%3A00%3A00%2B00%3A00%2F2020-10-31T12%3A00%3A00%2B00%3A00&limit=1000&filter=farea%3E5+AND+duration%3E20'

Read data

In addition to all the summary fields, the perm_results dict contains all the data. We can pass the data into geopandas to make it easier to interact with.

df = gpd.GeoDataFrame.from_features(perm_results["features"])
df
geometry ogc_fid n_pixels n_newpixels farea fperim flinelen duration pixden meanfrp t fid
0 POLYGON ((-118.98956 37.14274, -118.98955 37.1... 39758 238 0 7.326824 11.731364 0.000000 30.5 32.483377 0.000000 2020-10-24T00:00:00 6683
1 POLYGON ((-118.98956 37.14274, -118.98955 37.1... 39759 238 0 7.326824 11.731364 0.000000 30.5 32.483377 0.000000 2020-10-25T00:00:00 6683
2 POLYGON ((-118.98956 37.14274, -118.98956 37.1... 39760 237 0 7.326824 11.731364 0.000000 29.5 32.346892 0.000000 2020-10-08T12:00:00 6683
3 POLYGON ((-118.98956 37.14274, -118.98956 37.1... 39761 236 0 7.326824 11.731358 0.000000 26.5 32.210409 0.000000 2020-10-06T00:00:00 6683
4 POLYGON ((-118.98956 37.14273, -118.98956 37.1... 39762 235 1 7.268043 11.731136 0.000000 24.5 32.333326 0.580000 2020-10-03T00:00:00 6683
... ... ... ... ... ... ... ... ... ... ... ... ...
120 POLYGON ((-119.43342 37.31364, -119.43343 37.3... 40607 28801 139 1453.780326 270.752955 23.845096 27.5 19.811109 24.795108 2020-10-02T12:00:00 6280
121 POLYGON ((-119.43342 37.31364, -119.43342 37.3... 40608 28662 335 1451.772072 270.218766 32.202091 27.0 19.742769 2.427910 2020-10-02T00:00:00 6280
122 POLYGON ((-119.43343 37.31364, -119.43343 37.3... 40609 29114 313 1461.805801 274.300699 36.112503 28.0 19.916462 3.254313 2020-10-03T00:00:00 6280
123 POLYGON ((-119.43343 37.31365, -119.43343 37.3... 40610 29348 234 1467.245064 275.740728 34.356263 28.5 20.002112 19.402735 2020-10-03T12:00:00 6280
124 POLYGON ((-119.43343 37.31365, -119.43343 37.3... 40611 29677 329 1475.927568 277.331754 36.152268 29.0 20.107355 2.941368 2020-10-04T00:00:00 6280

125 rows × 12 columns

Explore data

We can quickly explore the data by setting the coordinate reference system (crs) and using .explore()

df = df.set_crs("EPSG:4326")
df.explore()
Make this Notebook Trusted to load map: File -> Trust Notebook

Visualize Most Recent Fire Perimeters with Firelines

If we wanted to combine collections to make more informative analyses, we can use some of the same principles.

First we’ll get the queryable fields:

fline_q = w.collection_queryables('public.eis_fire_fireline')
fline_q['properties']
{'wkb_geometry': {'$ref': 'https://geojson.org/schema/Geometry.json'},
 'ogc_fid': {'name': 'ogc_fid', 'type': 'number'},
 't': {'name': 't', 'type': 'string'},
 'fid': {'name': 'fid', 'type': 'number'}}

Read

Then we’ll use those fields to get most recent fire perimeters and fire lines.

most_recent_time = max(*perm['extent']['temporal']['interval']) 
print("Most Recent Time =", most_recent_time)

## Get the most recent fire perimeters
perm_results = w.collection_items(
    "public.eis_fire_perimeter",   
    datetime=most_recent_time,
    limit=1000,
    filter="farea>5",    
)
perimeters = gpd.GeoDataFrame.from_features(perm_results['features'])

## Get the most recent fire lines
perimeter_ids = perimeters.fid.unique()
perimeter_ids = ",".join(map(str, perimeter_ids)) 

fline_results = w.collection_items(
    "public.eis_fire_fireline",   
    datetime=most_recent_time,
    limit=1000,
    filter="fid IN (" + perimeter_ids + ")" # only the fires from the fire perimeter query above
    
)
fline = gpd.GeoDataFrame.from_features(fline_results['features'])
Most Recent Time = 2020-10-31T12:00:00+00:00

Visualize

perimeters = perimeters.set_crs("epsg:4326")
fline = fline.set_crs("epsg:4326")

m = perimeters.explore()
m = fline.explore(m=m, color="orange")
m
Make this Notebook Trusted to load map: File -> Trust Notebook